package com.ticket.sass.admin.business.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ticket.sass.admin.annotation.Module;
import com.ticket.sass.admin.business.AdminBusiness;
import com.ticket.sass.admin.config.AppConfig;
import com.ticket.sass.admin.dto.input.admin.*;
import com.ticket.sass.admin.dto.output.admin.AdminInfoOutput;
import com.ticket.sass.admin.dto.output.admin.AdminListOutput;
import com.ticket.sass.admin.model.PageModel;
import com.ticket.sass.admin.model.TokenModel;
import com.ticket.sass.admin.service.impl.AdminServiceImpl;
import com.ticket.sass.admin.service.impl.CinemaServiceImpl;
import com.ticket.sass.admin.service.impl.OperationLogServiceImpl;
import com.ticket.sass.admin.util.*;
import com.ticket.sass.common.entity.tenant.AdminEntity;
import com.ticket.sass.common.entity.tenant.CinemaEntity;
import com.ticket.sass.common.entity.tenant.OperationLogEntity;
import com.ticket.sass.common.exception.enums.ErrorCode;
import com.ticket.sass.common.exception.exception.BadRequestException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.TypeToken;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;


/**
 * 管理员业务
 * @author ywb
 * @date 2021-11-25 18:16
 */
@Slf4j
@Service
@AllArgsConstructor
public class AdminBusinessImpl implements AdminBusiness {
    
    final JwtUtil jwtUtil;
    final ModelUtil modelUtil;
    final RedisCacheUtil redisCache;
    final AdminServiceImpl adminService;
    final CinemaServiceImpl cinemaService;
    final OperationLogServiceImpl operationLogService;

    @Override
    public void add(AdminAddInput input){
        if (adminService.countByName(input.getUsername()) > 0){
            throw new BadRequestException(
                ErrorCode.RECORD_EXIST,
                EscapeUtil.errorMessage("username", "该用户名已存在")
            );
        }
        
        AdminEntity admin = modelUtil.mapToMap(input, AdminEntity.class);
        admin.setPassword(new BCryptUtil(AppConfig.PWD_SECRET).encrypt(admin.getPassword()));
        adminService.save(admin);
    }

    @Override
    public PageModel<AdminListOutput> list(AdminListInput input) {
        IPage<AdminEntity> page = adminService.iPage(input);
        List<AdminListOutput> outputs = modelUtil.listToList(
            page.getRecords(),
            new TypeToken<List<AdminListOutput>>(){}.getType()
        );
        return new PageModel<>(page.getTotal(), outputs);
    }

    @Override
    public void upd(AdminUpdInput input) {
        validateId(input.getId());
        
        if (adminService.countByNameNotId(input.getUsername(), input.getId()) > 0){
            throw new BadRequestException(
                ErrorCode.RECORD_EXIST,
                EscapeUtil.errorMessage("account", "该账户名已存在")
            );
        }
        AdminEntity adminEntity = modelUtil.mapToMap(input, AdminEntity.class);
        adminService.updateById(adminEntity);

//        String cacheObject = redisCache.getCacheObject(jwtUtil.validTokenCacheKey(adminEntity.getId()));
//        AdminInfoOutput admin = GsonUtil.getInstance().fromJson(cacheObject, AdminInfoOutput.class);
//        if (Objects.nonNull(admin)){
//            admin.setEnable(adminEntity.getEnable());
//            redisCache.setCacheObject(jwtUtil.validTokenCacheKey(admin.getId()), admin.toJson(), 2, TimeUnit.DAYS);
//        }
        
        // 只要修改了 即需要重新登录
        redisCache.deleteObject(jwtUtil.validTokenCacheKey(adminEntity.getId()));
    }

    @Override
    public TokenModel login(AdminLoginInput input) {
        if (adminService.countByName(input.getUsername()) > 1) {
            throw new BadRequestException(
                ErrorCode.SQL_DATA_ERROR,
                EscapeUtil.errorMessage("username", input.getUsername() + "存在多个账户！")
            );
        }
        // 用户名 是 唯一的 根据用户名查询到用户
        AdminEntity admin = adminService.getOne(new QueryWrapper<AdminEntity>().eq("username", input.getUsername()));

        // 登录成功
        if (
            Objects.nonNull(admin) &&
            admin.getEnable() &&
            new BCryptUtil(AppConfig.PWD_SECRET).check(input.getPassword(), admin.getPassword())
        ){
            AdminInfoOutput output = toOutput(admin);
            // 成功存入redis 中 将权限一起保存
            redisCache.setCacheObject(jwtUtil.validTokenCacheKey(admin.getId()), output.toJson(), 2, TimeUnit.DAYS);
        } else {
            throw new BadRequestException(
                ErrorCode.LOGIN_PARAMS_ERROR,
                ErrorCode.LOGIN_PARAMS_ERROR.getReasonPhrase()
            );
        }
        operationLogService.save(new OperationLogEntity(admin.getId(), Module.ADMIN.toString(), "管理员登录", input.toJsonString(), Boolean.TRUE));
        
        return jwtUtil.createToken(admin.getId(), admin.getUsername());
    }
    
    @Override
    public AdminInfoOutput info() {
        Long id = jwtUtil.getAudBy(ResponseUtil.getRequest());
        AdminEntity admin = adminService.getById(id);
        return toOutput(admin);
    }
    
    @Override
    public void updPassword(AdminUpdPasswordInput input) {
        validateId(input.getId());
        AdminEntity admin = new AdminEntity(input.getId(), new BCryptUtil(AppConfig.PWD_SECRET).encrypt(input.getNewPassword()));
        adminService.updateById(admin);
        // 删除缓存
        redisCache.deleteObject(jwtUtil.validTokenCacheKey(admin.getId()));
    }
    
    private void validateId(Long id) {
        if (adminService.isEmpty(id)){
            throw new BadRequestException(
                ErrorCode.RECORD_NOT_EXIST,
                EscapeUtil.errorMessage("id", ErrorCode.RECORD_NOT_EXIST.getReasonPhrase())
            );
        }
    }
    
    private AdminInfoOutput toOutput(AdminEntity admin) {
        Long tenantId = admin.getTenantId();
        List<AdminInfoOutput.CinemaBrief> cinemaBriefs = modelUtil.listToList(
            cinemaService.list(new QueryWrapper<CinemaEntity>().eq("tenant_id", tenantId)),
            new TypeToken<List<AdminInfoOutput.CinemaBrief>>() {}.getType()
        );
        
        return new AdminInfoOutput(admin.getId(), admin.getUsername(), admin.getEnable(), tenantId, cinemaBriefs);
    }
}
